package com.icee.myth.manager.channelHandler;

import com.icee.myth.utils.LogConsts;
import com.icee.myth.utils.MLogger;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.*;
import org.jboss.netty.handler.codec.http.*;
import org.jboss.netty.handler.codec.http.cookie.ServerCookieDecoder;
import org.jboss.netty.handler.codec.http.cookie.ServerCookieEncoder;
import org.jboss.netty.util.CharsetUtil;

import java.io.*;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.*;
import static org.jboss.netty.handler.codec.http.HttpHeaders.isKeepAlive;
import static org.jboss.netty.handler.codec.http.HttpResponseStatus.OK;
import static org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1;

/**
 * Http服务器处理
 * @author yangyi
 */
public class HttpServerHandler extends SimpleChannelUpstreamHandler {

    public static final String HTTP_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz";
    public static final String HTTP_DATE_GMT_TIMEZONE = "GMT";
    public static final int HTTP_CACHE_SECONDS = 60;

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
            throws Exception {
        e.getCause().printStackTrace();
        e.getChannel().close();
    }

    public static void writeResponse(HttpRequest request, Channel channel, String contentType, String content, CookieEncoder newCookieEncoder) {
        // Decide whether to close the connection or not.
        boolean keepAlive = isKeepAlive(request);

        // Build the response object.
        HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
        response.setContent(ChannelBuffers.copiedBuffer(content, CharsetUtil.UTF_8));
//        response.setHeader(CONTENT_TYPE, contentType);
        response.headers().set(CONTENT_TYPE, contentType);

        if (keepAlive) {
            // Add 'Content-Length' header only for a keep-alive connection.
//            response.setHeader(CONTENT_LENGTH, response.getContent().readableBytes());
            response.headers().set(CONTENT_LENGTH, response.getContent().readableBytes());
            // Add keep alive header as per http://www.w3.org/Protocols/HTTP/1.1/draft-ietf-http-v11-spec-01.html#Connection
//            response.setHeader(CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
            response.headers().set(CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
        }

        // Encode the cookie.
//        String cookieString = request.getHeader(COOKIE);
        String cookieString = request.headers().get(COOKIE);
        if (cookieString != null) {
//            CookieDecoder cookieDecoder = new CookieDecoder();
            ServerCookieDecoder cookieDecoder = ServerCookieDecoder.STRICT;
//            Set<Cookie> cookies = cookieDecoder.decode(cookieString);
            Set<org.jboss.netty.handler.codec.http.cookie.Cookie> cookies = cookieDecoder.decode(cookieString);
            if (!cookies.isEmpty()) {
                // Reset the cookies if necessary.
//                CookieEncoder cookieEncoder = new CookieEncoder(true);
                ServerCookieEncoder cookieEncoder = ServerCookieEncoder.STRICT;

                for (org.jboss.netty.handler.codec.http.cookie.Cookie cookie : cookies) {
//                    cookieEncoder.addCookie(cookie);
                    cookieEncoder.encode(cookie);
                }
//                response.addHeader(SET_COOKIE, cookieEncoder.encode());
                response.headers().add(SET_COOKIE, cookieEncoder.encode());
            }
        }

        if (newCookieEncoder != null) {
//            response.addHeader(SET_COOKIE, newCookieEncoder.encode());
            response.headers().add(SET_COOKIE, newCookieEncoder.encode());
        }

        // Write the response.
        ChannelFuture future = channel.write(response);
        MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_DEBUG, content);

        // Close the non-keep-alive connection after the write operation is done.
        if (!keepAlive) {
            future.addListener(ChannelFutureListener.CLOSE);
        }
    }

    public static int getRemoteIP(Channel channel) {
        InetSocketAddress saddr = (InetSocketAddress) channel.getRemoteAddress();
        InetAddress inetAddress = saddr.getAddress();
        byte[] bs = inetAddress.getAddress();
        int ip = 0;
        if (bs.length == 4) {
            ip = (bs[0] & 0xFF) << 24 | (bs[1] & 0xFF) << 16 | (bs[2] & 0xFF) << 8 | (bs[3] & 0xFF);
        }
        return ip;
    }

    /**
     * When file timestamp is the same as what the browser is sending up, send a "304 Not Modified"
     *
     * @param ctx Context
     */
    public static void sendNotModified(ChannelHandlerContext ctx) {
        HttpResponse response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.NOT_MODIFIED);
        setDateHeader(response);

        // Close the connection as soon as the error message is sent.
        ctx.getChannel().write(response).addListener(ChannelFutureListener.CLOSE);
    }

    public static void showViewPage(HttpRequest request, MessageEvent e, String viewName, CookieEncoder newCookieEncoder) {
        BufferedReader bufReader = null;
        try {
            //            bufReader = new BufferedReader(new FileReader("views/" + viewName + ".html"));//读中文文件乱码问题
            InputStreamReader isr = new InputStreamReader(new FileInputStream("views/" + viewName + ".html"), "UTF-8");
            bufReader = new BufferedReader(isr);

            StringBuilder buf = new StringBuilder();
            String text;
            while ((text = bufReader.readLine()) != null) {
                buf.append(text).append("\r\n");
            }

            writeResponse(request, e.getChannel(), "text/html; charset=UTF-8", buf.toString(), newCookieEncoder);
        } catch (IOException ex) {
            Logger.getLogger(HttpServerHandler.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            if (null != bufReader) {
                try {
                    bufReader.close();
                } catch (Exception ex) {
                    Logger.getLogger(HttpServerHandler.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
    }

    /**
     * Sets the Date header for the HTTP response
     *
     * @param response HTTP response
     */
    public static void setDateHeader(HttpResponse response) {
        SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US);
        dateFormatter.setTimeZone(TimeZone.getTimeZone(HTTP_DATE_GMT_TIMEZONE));

        Calendar time = new GregorianCalendar();
//        response.setHeader(HttpHeaders.Names.DATE, dateFormatter.format(time.getTime()));
        response.headers().set(HttpHeaders.Names.DATE, dateFormatter.format(time.getTime()));
    }

    /**
     * Sets the Date and Cache headers for the HTTP Response
     *
     * @param response    HTTP response
     * @param fileToCache file to extract content type
     */
    public static void setDateAndCacheHeaders(HttpResponse response, File fileToCache) {
        SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US);
        dateFormatter.setTimeZone(TimeZone.getTimeZone(HTTP_DATE_GMT_TIMEZONE));

        // Date header
        Calendar time = new GregorianCalendar();
//        response.setHeader(HttpHeaders.Names.DATE, dateFormatter.format(time.getTime()));
        response.headers().set(HttpHeaders.Names.DATE, dateFormatter.format(time.getTime()));

        // Add cache headers
        time.add(Calendar.SECOND, HTTP_CACHE_SECONDS);
//        response.setHeader(HttpHeaders.Names.EXPIRES, dateFormatter.format(time.getTime()));
//        response.setHeader(HttpHeaders.Names.CACHE_CONTROL, "private, max-age=" + HTTP_CACHE_SECONDS);
//        response.setHeader(HttpHeaders.Names.LAST_MODIFIED, dateFormatter.format(new Date(fileToCache.lastModified())));
        response.headers().set(HttpHeaders.Names.EXPIRES, dateFormatter.format(time.getTime()));
        response.headers().set(HttpHeaders.Names.CACHE_CONTROL, "private, max-age=" + HTTP_CACHE_SECONDS);
        response.headers().set(HttpHeaders.Names.LAST_MODIFIED, dateFormatter.format(new Date(fileToCache.lastModified())));
    }


    public static void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) {
        HttpResponse response = new DefaultHttpResponse(HTTP_1_1, status);
//        response.setHeader(CONTENT_TYPE, "text/plain; charset=UTF-8");
        response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8");
        response.setContent(ChannelBuffers.copiedBuffer(
                "Failure: " + status.toString() + "\r\n",
                CharsetUtil.UTF_8));

        // Close the connection as soon as the error message is sent.
        ctx.getChannel().write(response).addListener(ChannelFutureListener.CLOSE);
    }

//     public static void handleFileRequest(ChannelHandlerContext ctx, MessageEvent e, final String path, CookieEncoder cookieEncoder) throws Exception {
//        HttpRequest request = (HttpRequest) e.getMessage();
//        if (path == null) {
//            sendError(ctx, FORBIDDEN);
//            return;
//        }
//        File file = new File(path);
//        if (file.isHidden() || !file.exists()) {
//            sendError(ctx, NOT_FOUND);
//            return;
//        }
//        if (!file.isFile()) {
//            sendError(ctx, FORBIDDEN);
//            return;
//        }
//
//        // Cache Validation
//        String ifModifiedSince = request.getHeader(HttpHeaders.Names.IF_MODIFIED_SINCE);
//        if (ifModifiedSince != null && !ifModifiedSince.equals("")) {
//            SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US);
//            Date ifModifiedSinceDate = dateFormatter.parse(ifModifiedSince);
//
//            // Only compare up to the second because the datetime format we send to the client does not have milliseconds
//            long ifModifiedSinceDateSeconds = ifModifiedSinceDate.getTime() / 1000;
//            long fileLastModifiedSeconds = file.lastModified() / 1000;
//            if (ifModifiedSinceDateSeconds == fileLastModifiedSeconds) {
//                sendNotModified(ctx);
//                return;
//            }
//        }
//
////        RandomAccessFile raf;
////        try {
////            raf = new RandomAccessFile(file, "r");
////        } catch (FileNotFoundException fnfe) {
////            sendError(ctx, NOT_FOUND);
////            return;
////        }
////        long fileLength = raf.length();
//        StringBuffer contents = new StringBuffer();
//        BufferedReader reader = null;
//         reader = new BufferedReader(new FileReader(file));
//         String text = null;
//         while((text = reader.readLine()) != null){
//             contents.append(text);
//             contents.append(System.getProperty("line.separator"));
//         }
//        writeResponse(request, ctx.getChannel(), Manager.INSTANCE.mimeTypesMap.getContentType(file), contents.toString(), cookieEncoder);
//
//
////        HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
////        setContentLength(response, fileLength);
////        setContentTypeHeader(response, file);
////        setDateAndCacheHeaders(response, file);
////
////        Channel ch = e.getChannel();
////
////        // Write the initial line and the header.
////        ch.write(response);
////
////        // Write the content.
////        ChannelFuture writeFuture;
////        if (ch.getPipeline().get(SslHandler.class) != null) {
////            // Cannot use zero-copy with HTTPS.
////            writeFuture = ch.write(new ChunkedFile(raf, 0, fileLength, 8192));
////        } else {
////            // No encryption - use zero-copy.
////            final FileRegion region =
////                    new DefaultFileRegion(raf.getChannel(), 0, fileLength);
////            writeFuture = ch.write(region);
////            writeFuture.addListener(new ChannelFutureProgressListener() {
////
////                public void operationComplete(ChannelFuture future) {
////                    region.releaseExternalResources();
////                }
////
////                public void operationProgressed(
////                        ChannelFuture future, long amount, long current, long total) {
////                    System.out.printf("%s: %d / %d (+%d)%n", path, current, total, amount);
////                }
////            });
////        }
////
////        // Decide whether to close the connection or not.
////        if (!isKeepAlive(request)) {
////            // Close the connection when the whole content is written out.
////            writeFuture.addListener(ChannelFutureListener.CLOSE);
////        }
//    }

    /**
     * Sets the content type header for the HTTP Response
     *
     * @param response HTTP response
     * @param file     file to extract content type
     */
//    public static void setContentTypeHeader(HttpResponse response, File file) {
//        response.setHeader(HttpHeaders.Names.CONTENT_TYPE, Manager.INSTANCE.mimeTypesMap.getContentType(file/*.getPath()*/));
//    }

    public static void sendErrorNoPrivilege(HttpRequest request, MessageEvent e) {
        writeResponse(request, e.getChannel(), "text/html; charset=UTF-8", "{\"result\":" + -1 + ", \"error\": " + "\"权限不够\"}", null);
    }

}
